﻿using MediatR;
using Service.Auth.Api.Application.Command;
using Service.Auth.Api.Application.Notification;
using Service.Auth.Api.Application.Query;
using Service.Auth.Domain;
using Service.Auth.Domain.AggregatesModel;
using Service.Auth.Domain.Events;
using Service.Auth.Infrastructure.Utils;
using Service.Auth.Model.Model.RequestDto.User;
using Service.Core.Identity.Login;
using Service.Core.Redis.Aop.Cache;
using Service.Core.Redis.Services;
using Service.Core.Repository;
using Service.Core.Repository.Attribute;
using Service.Framework;
using Service.Framework.Utils;

namespace Service.Auth.Api.Application.Handler.CommandHandler;

/// <summary>
/// 
/// </summary>
/// <param name="userRepository"></param>
/// <param name="userQueryService"></param>
/// <param name="userSession"></param>
/// <param name="redisService"></param>
public class UserCommandHandler(
    IMediator mediator, IRepository<User, long> userRepository, UserQueryService userQueryService, ILoginUserSession loginUserSession
    , IRedisService redisService)
    : IRequestHandler<CreateUserCommand, bool>,
    IRequestHandler<CreateUserByPhoneCommand, long>,
         IRequestHandler<UpdateUserCommand, bool>,
        IRequestHandler<ChangePasswordCommand, bool>,
        IRequestHandler<DeleteUserCommand, bool>,
        IRequestHandler<SetUserStatusCommand, bool>,
        IRequestHandler<UpdateUserByLoginCommand, bool>,
    IRequestHandler<SmsChangePasswordCommand, bool>,
    IRequestHandler<LogoutLoginCommand,bool>
{
    /// <inheritdoc/>
    [UnitOfWork]
    public async Task<bool> Handle(CreateUserCommand request, CancellationToken cancellationToken)
    {
        var userDto = request.User;
        if (await userRepository.AnyAsync(s => s.Account.Equals(userDto.Account), cancellationToken))
            throw new LogicErrorException($"账号：{userDto.Account}已存在，请修改后再试。");
        var passwordValueObject = new PasswordValueObject(userDto.Password);
        var user = new User(userDto.Account, passwordValueObject, userDto.RealName, userDto.PhoneText);
        user.SetSystemRoles(userDto.Systems, userDto.Roles);
        var result = await userRepository.InsertAsync(user, cancellationToken);
        return result;
    }


    /// <summary>
    /// 
    /// </summary>
    /// <param name="request"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <exception cref="LogicErrorException"></exception>
    [UnitOfWork]
    public async Task<long> Handle(CreateUserByPhoneCommand request, CancellationToken cancellationToken)
    {
        var userDto = request.User;
        if (await userRepository.AnyAsync(s => s.Phone.Equals(userDto.PhoneText), cancellationToken))
            throw new LogicErrorException($"手机号：{userDto.PhoneText}已存在，请修改后再试。");
        var passwordValueObject = new PasswordValueObject(userDto.Password);
        var user = new User(userDto.Account, passwordValueObject, userDto.RealName, userDto.PhoneText);
        user.SetSystemRoles(userDto.Systems, userDto.Roles);
        var result = await userRepository.InsertAsync(user, cancellationToken);
        return user.Id;
    }


    /// <summary>
    /// 
    /// </summary>
    /// <param name="request"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <exception cref="LogicErrorException"></exception>
    [UnitOfWork]
    public async Task<bool> Handle(ChangePasswordCommand request, CancellationToken cancellationToken)
    {
        var requestChangePassword = request.ChangePassword;
        var user = await userRepository.GetByIdAsync(long.Parse(loginUserSession.IdentityName), cancellationToken) ??
                   throw new LogicErrorException($"密码重置失败，当前用户不存在，请联系管理员。");
        var key = $"{nameof(ChangePasswordCommand)}:{user.Id}";
        var updateCount = await redisService.Database.StringGetAsync(key);
        var count = 0;
        try
        {
            if (updateCount.HasValue)
            {
                count = (int)updateCount;
            }
            var maxCount = 5;
            if (count >= maxCount)
            {
                throw new LogicErrorException($"密码修改失败，当日剩余修改次数已用完。");
            }
            if (!(HashCrypto.GeneratePwd(requestChangePassword.OldPwd, user.PasswordValueObject.Salt)).Equals((string)user.PasswordValueObject))
            {
                throw new LogicErrorException($"密码修改错误，旧密码不一致，当日剩余修改次数{maxCount - count}");
            }
            user.ChangePassword(requestChangePassword.NewPwd);
            return await userRepository.UpdateAsync(user, cancellationToken);
        }
        finally
        {
            count++;
            await redisService.Database.StringSetAsync(key, count, TimeSpan.FromMinutes(12));
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="request"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <exception cref="LogicErrorException"></exception>
    [UnitOfWork]
    [RemoveRedisCacheInterceptor(cacheKey:  "User:{userId}")]
    public async Task<bool> Handle(DeleteUserCommand request, CancellationToken cancellationToken)
    {
        var userDto = request.User;
        var user = await userRepository.GetByIdAsync(userDto.UserId, cancellationToken) ??
                   throw new LogicErrorException($"当前用户不存在，请联系管理员。");
        if (loginUserSession.IdentityName.Equals(user.Id.ToString()))
        {
            throw new LogicErrorException($"当前用户不能修改自己的状态，请联系管理员。");
        }
        user.AddDomainEvent(new LogoutAccountNotification(userDto.UserId));
        return await userRepository.SoftDeleteAsync(user, cancellationToken);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="request"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <exception cref="LogicErrorException"></exception>
    [UnitOfWork]
    public async Task<bool> Handle(UpdateUserCommand request, CancellationToken cancellationToken)
    {
        var userDto = request.User;
        if (await userRepository.AnyAsync(s => s.Account.Equals(userDto.Account) && userDto.Id != s.Id, cancellationToken))
            throw new LogicErrorException($"账号：{userDto.Account}已存在，请修改后再试。");
        var user = await userQueryService.QueryUserById(userDto.Id);
        user.SetAccount(userDto.Account);
        if (!userDto.PhoneText.Contains('*'))
            user.Phone = userDto.PhoneText;
        user.RealName = userDto.RealName;
        user.SetSystemRoles(userDto.Systems, userDto.Roles);
        user.AddDomainEvent(new ChangeUserDomainEvent(user));
        var result = await userRepository.UpdateAsync(user, cancellationToken);
        return result;
    }

    /// <summary>
    /// 禁用用户
    /// </summary>
    /// <param name="request"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <exception cref="LogicErrorException"></exception>
    [UnitOfWork]
    public async Task<bool> Handle(SetUserStatusCommand request, CancellationToken cancellationToken)
    {
        var setUserStatusDto = request.SetUserStatusDto;
        var user = await userRepository.GetByIdAsync(setUserStatusDto.Id, cancellationToken);
        if (loginUserSession.IdentityName.Equals(user.Id.ToString()))
        {
            throw new LogicErrorException($"当前用户不能修改自己的状态，请联系管理员。");
        }
        user.SetUserStatus(setUserStatusDto.UsetStatus);
        var result = await userRepository.UpdateAsync(user, cancellationToken);
        await redisService.Database.KeyDeleteAsync($"User:{user.Id}");
        return result;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="request"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    [UnitOfWork]
    public async Task<bool> Handle(UpdateUserByLoginCommand request, CancellationToken cancellationToken)
    {
        var userDto = request.User;

        var userId = long.Parse(loginUserSession.IdentityName);
        var user = await userQueryService.QueryUserById(userId);
        user.SetRealName(userDto.RealName);
        user.SetPicture(userDto.Picture);
        user.AddDomainEvent(new ChangeUserDomainEvent(user));
        await userRepository.UpdateAsync(user, cancellationToken);
        if (!userDto.OldPwd.IsNullOrWhiteSpace() && !userDto.NewPwd.IsNullOrWhiteSpace())
        {
            await mediator.Send(new ChangePasswordCommand(new ChangePasswordDto()
            {
                OldPwd = userDto.OldPwd,
                NewPwd = userDto.NewPwd
            }), cancellationToken);
        }
        return true;
    }

    [UnitOfWork]
    public async Task<bool> Handle(SmsChangePasswordCommand request, CancellationToken cancellationToken)
    {
        var smsChangePasswordDto = request.SmsChangePassword;
        var user = await userQueryService.QueryUserByAccount(smsChangePasswordDto.Phone) ??
                   throw new LogicErrorException($"密码重置失败，当前用户不存在，请联系管理员。");
        var key = $"{nameof(ChangePasswordCommand)}:{user.Id}";
        var updateCount = await redisService.Database.StringGetAsync(key);
        var count = 0;
        try
        {
            if (updateCount.HasValue)
            {
                count = (int)updateCount;
            }
            var maxCount = 5;
            if (count >= maxCount)
            {
                throw new LogicErrorException($"密码修改失败，当日剩余修改次数已用完。");
            }
            user.ChangePassword(smsChangePasswordDto.NewPwd);
            return await userRepository.UpdateAsync(user, cancellationToken);
        }
        finally
        {
            count++;
            await redisService.Database.StringSetAsync(key, count, TimeSpan.FromMinutes(12));
        }
    }

    [RemoveRedisCacheInterceptor(cacheKey:  "User:{userId}")]
    public async Task<bool> Handle(LogoutLoginCommand request, CancellationToken cancellationToken)
    {
        return await Task.FromResult(true);
    }
}